﻿using System;

namespace GameBasic
{
    /// <summary>
    /// 32bits wrapper of <typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public struct Enum32<T> : IEquatable<Enum32<T>>, IEquatable<long> where T : unmanaged, Enum
    {
        public int value;

        public T TValue
        {
            get
            {
                unsafe
                {
                    fixed (void* ptr = &value)
                    {
                        return *(T*)ptr;
                    }
                }
            }
            set
            {
                unsafe
                {
                    fixed (void* ptr = &this.value)
                    {
                        *(T*)ptr = value;
                    }
                }
            }
        }

        public Enum32(int value)
        {
            this.value = value;
        }

        public Enum32(T t)
        {
            unsafe
            {
                fixed (int* ptr = &this.value)
                {
                    *(T*)ptr = t;
                }
            }
        }

        #region Compare
        public bool Equals(Enum32<T> other)
        {
            return value == other.value;
        }

        public bool Equals(long other)
        {
            return value == other;
        }

        public override bool Equals(object obj)
        {
            return obj is Enum32<T> i && i.value == value;
        }

        public override int GetHashCode()
        {
            return value.GetHashCode();
        }
        #endregion

        #region Bits
        public bool Any(Enum32<T> flags)
        {
            return (value & flags.value) != 0;
        }

        public bool Any(int flags)
        {
            return (value & flags) != 0;
        }

        public bool All(Enum32<T> flags)
        {
            return (value & flags.value) == flags.value;
        }

        public bool All(Enum32<T> flags, Enum32<T> ignores)
        {
            flags.value &= ~ignores.value;
            return (value & flags.value) == flags.value;
        }

        public bool All(int flags)
        {
            return (value & flags) == flags;
        }

        public bool All(int flags, int ignores)
        {
            flags &= ~ignores;
            return (value & flags) == flags;
        }

        public void SetOn(Enum32<T> flags)
        {
            value |= flags.value;
        }

        public void SetOn(int flags)
        {
            value |= flags;
        }

        public void SetOff(Enum32<T> flags)
        {
            value &= ~flags.value;
        }

        public void SetOff(int flags)
        {
            value &= ~flags;
        }
        #endregion

        public static bool operator ==(Enum32<T> left, Enum32<T> right) => left.value == right.value;
        public static bool operator !=(Enum32<T> left, Enum32<T> right) => left.value != right.value;

        // int
        public static implicit operator int(Enum32<T> v) => v.value;
        public static implicit operator Enum32<T>(int v) => new(v);
        // uint
        public static implicit operator uint(Enum32<T> v) => (uint)v.value;
        public static implicit operator Enum32<T>(uint v) => new((int)v);
        // T
        public static implicit operator T(Enum32<T> v) => v.TValue;
        public static implicit operator Enum32<T>(T v) => new(v);
    }
}